package cn.ant.config;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

/***
 * 配置RabbitMQ发布确认回调类；分别为发送到交换机的回调和交换机发送到队列的回调
 * @author Anhui AntLaddie <a href="https://juejin.cn/user/4092805620185316">(掘金蚂蚁小哥)</a>
 * @version 1.0
 **/
@Slf4j
@Component
@RequiredArgsConstructor
public class RabbitMQMyCallBack implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback {

    // 注入rabbitTemplate对象
    private final RabbitTemplate rabbitTemplate;

    /***
     * 对象实例化完成（对象创建和属性注入）后调用此方法
     */
    @PostConstruct
    public void init() {
        // 设置发布确认信息回调类RabbitTemplate.ConfirmCallback confirmCallback;
        rabbitTemplate.setConfirmCallback(this);
        // 设置回退消息回调类ReturnsCallback.ReturnsCallback returnsCallback;
        rabbitTemplate.setReturnsCallback(this);
        // true：交换机无法将消息进行路由时，会将该消息返回给生产者；
        // false：如果发现消息无法进行路由，则直接丢弃。
        rabbitTemplate.setMandatory(true);  // 或使用配置 spring.rabbitmq.template.mandatory: true
    }

    /***
     * 发布确认（生产者-->交换机的确认）
     * @param correlationData 回调的相关数据
     * @param ack 消息是否成功发送给交换机，true成功，false失败
     * @param cause 对于ack为false时会有对应的失败原因，否则为空
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        // 获取对应的ID消息，因为不确认是否有ID被传入，所以取值需要判空
        String id = correlationData == null ? "" : correlationData.getId();
        // 校验是否成功发送
        if (ack) {
            log.info("消息已经成功交给了交换机，对应消息ID为：{}", id);
        } else {
            log.info("消息未能成功发送给交换机，对应消息ID为：{}，异常原因：{}", id, cause);
        }
    }

    /***
     * 注意：由于设置了备份交换机，无法路由的消息也不会走这段代码，RabbitMQ会将消息直接丢给备份交换机
     *
     * 当消息无法被路由时执行当前回调
     * @param returned 被退回的消息信息
     */
    @Override
    public void returnedMessage(ReturnedMessage returned) {
        // // 发送的消息
        // Message message = returned.getMessage();
        // // 发送到哪个交换机
        // String exchange = returned.getExchange();
        // // 交换机到队列的路由key
        // String routingKey = returned.getRoutingKey();
        // // 退回原因
        // String replyText = returned.getReplyText();
        // // 退回原因状态码
        // int replyCode = returned.getReplyCode();
        // // 消息打印
        // log.info("信息被回退，从交换机：{} 路由：{} 发送到队列失败，发送信息为：{}，退回状态码：{} 和原因：{}",
        //         exchange, routingKey, message, replyCode, replyText);
        // // 我们可以在这后面对发送失败的消息进行处理......(不过这里我配置了备份交换机了)
    }
}


